﻿using System;
using Fetches.Fetch.Dto;
using Fetches.Fetch.Factory;
using Fetches.Fetch.Factory.Persistence;
using Fetches.Fetch.Utils;
using FetchesDeploy.Distributed.DataPersistence;
using Microsoft.Extensions.Configuration;

namespace FetchesDeploy.Distributed.Factory
{
    /// <summary>
    /// 可伸缩采集工厂的伸缩调整单位实例的容器
    /// </summary>
    public class FetchFactoryUnit
    {
        private const string WaitingMessage = "FactoryUnit正在启动中,请稍候";
        public static string Message { get; private set; } = WaitingMessage;

        // 定义一个标识确保线程同步
        private static readonly object Padlock = new object();
        private static FetchFactoryUnit _instance;

        private string _endpoint;

        public long FactoryId
        {
            get
            {
                if (_factory == null)
                {
                    return 0;
                }
                return _factory.UnitSchedules.Id;
            }
        }

        private IConfiguration _configuration;
        private IRuntimeDataService _runtimeDataService;
        private LocalFetchFactory _factory;

        /// <summary>
        /// 单例FactoryUnit
        /// </summary>
        /// <returns></returns>
        public static FetchFactoryUnit Instance()
        {
            if (_instance == null)
            {
                lock (Padlock)
                {
                    if (_instance == null)
                    {
                        _instance = new FetchFactoryUnit();
                    }
                }
            }
            return _instance;
        }

        public void Configure(IConfiguration configuration)
        {
            _configuration = configuration;
        }

        public void Run()
        {
            try
            {
                Message = WaitingMessage;
                Serving();
                Message = "welcome to fetches!";
            }
            catch (Exception e)
            {
                Message = $"FactoryUnit启动失败，请检查配置后重启服务。{e.Message}";
            }
        }

        public void Shutdown()
        {
            var scheduleInfo = _factory.UnitSchedules;
            _factory.Shutdown();

            Message = "FactoryUnit已关闭!";
        }

        #region 内部服务方法



        private void Serving()
        {
            //参考dubbo服务注册时的地址问题,暴露合适的地址给控制中心调用是个麻烦的点
            //这里地址算法并不完善，还可以考虑环境变量、多地址等办法注册
            var ip = HostingHelper.GetLocalIPv4();

            if (_configuration.GetValue<bool>("LocalMode"))
            {
                //本地模式下单个Container的运行就是一个完整的爬虫生命周期，可以作为Crawlab中的一个爬虫上传部署
                //读取配置好的Order并执行，结果写入配置的目标地址，然后退出
                _runtimeDataService = new StandaloneRuntimeDataService();
            }
            else
            {
                _runtimeDataService = new EtcdRuntimeDataService(_configuration.GetValue<string>("EtcdUrls"));
                if (_configuration.GetValue<bool>("EtcdPositiveWatch"))
                {
                    //正常应该控制中心主动调用Container的http地址分配任务
                    //PositiveWatch的话container通过watch etcd中对自己对应任务key的变化来获取任务，这样控制中心的调度方法也要响应修改 
                    //这种设计存在任务分配丢失的风险，还不如采取redis队列的方式来处理
                    //使用redis队列的话一个fetcher类型一个队列,缺点是不能指定由哪个container执行。
                    //((EtcdRuntimeDataService)runtimeDataService).WatchFetcherDispatch(workshop,acton);
                }
            }


            //这个设置在了项目的调试设置里面了
            if (_configuration.GetValue<bool>("EasyDebugMode"))
            {
                // _runtimeEnv.EasyDebugMode = true;
            }

            var port = _configuration.GetValue<int>("Port");
            _endpoint = $"http://{ip}:{port}";
            try
            {
                _factory = new LocalFetchFactory(_runtimeDataService, _endpoint);
            }
            catch (Exception e)
            {
                throw e;
            }
        }

        #endregion

        #region 转接工具方法

        public void ChangeFactoryConfig(FactoryConfig workshopConfig)
        {
            _runtimeDataService.WriteFactoryConfig(workshopConfig);
        }

        /// <summary>
        /// 基本等同LocalFactory的Fetch方法，仅测试或LocalMode使用
        /// </summary>
        /// <param name="order"></param>
        /// <returns></returns>
        public FetchReceipt TakeFetchOrder(FetchOrder order)
        {
            return _factory.TakeFetchOrder(order);
        }

        public void DispatchFetcher(long fetcherId)
        {
            _factory.DispatchFetcher(fetcherId);
        }

        public void DismissFetcher(long fetcherId)
        {
            _factory.DismissFetcher(fetcherId);
        }

        public void StopFetch(long taskId)
        {
            var task = _runtimeDataService.GetFetchTaskByTaskId(taskId);
            _factory.StopFetch(new FetchReceipt() { Id = taskId, FetcherIds = task.FetcherIds });
        }

        public void ContinueFetch(long taskId)
        {
            var task = _runtimeDataService.GetFetchTaskByTaskId(taskId);
            _factory.ContinueFetch(new FetchReceipt() { Id = taskId, FetcherIds = task.FetcherIds });
        }
        #endregion
    }
}
